package com.lanzuo.pana.adapter.client.watcher;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lanzuo.adapter.common.constant.CommandConstant;
import com.lanzuo.pana.adapter.record.domain.ProgramDeploymentRecord;
import com.lanzuo.pana.adapter.record.service.ProgramDeploymentRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.mina.core.session.IoSession;

import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 启动该线程后，需存入一个容器管理它的生命周期；
 * 注意：
 * 若连接信息发生变更着需要停止该线程，并重新初始化&启动。
 */
@Slf4j
public class ChangeoverWatcher implements Runnable {

    SshConfig sshConfig;

    boolean running = true;

    ProgramDeploymentRecordService deploymentRecordService;

    IoSession ioSession;

    public ChangeoverWatcher(SshConfig config, IoSession session) {
        this.sshConfig = config;
        this.ioSession = session;
        deploymentRecordService = SpringUtil.getBean(ProgramDeploymentRecordService.class);
    }

    @Override
    public void run() {
        LanzuoChannelSftp sftpChannel;
        try {
            SshConnector sshConnector = new SshConnector(sshConfig);
            sftpChannel = sshConnector.getClient();
        } catch (Exception e) {
            log.error("ssh文件夹连接异常， {}", e.getMessage());
            // 记录异常信息
            throw new RuntimeException(e.getMessage());
        }

        log.info("Starting watching ssh directory, remote dir: {}", sftpChannel.getConfig().getRemotePath());

        String basePath = sftpChannel.getConfig().getRemotePath();

        // 持续运行 running = false，则ssh协议退出采集
        while (running) {
            try {
                Deque<SftpFile> sftpFiles = sftpChannel.find(sftpChannel.getConfig().getRemotePath(),
                        sftpChannel.getConfig().getLogType(), sftpChannel.getConfig().getExtraArgs(), null);

                log.info("Got {} db files in {}", sftpFiles.size(), sftpChannel.getConfig().getRemotePath());
                SftpFile sftpFile;

                List<String> msjPath = new ArrayList<>();
                List<Pair<String, String>> originPath = new ArrayList<>();
                List<Pair<String, String>> machineMsjPath = new ArrayList<>();

                while ((sftpFile = sftpFiles.pollFirst()) != null) {
                    String currentFilePath = sftpFile.getFilePath();
                    String fileName = Paths.get(currentFilePath).getFileName().toString();
                    log.info("fileName: {}", fileName);

                    // 去除文件名，保留路径
                    currentFilePath = currentFilePath.replace(fileName, "");

                    String msjId = fileName.split("\\.")[0];
                    if (basePath.equals(currentFilePath)) {
                        msjPath.add(msjId);
                        originPath.add(Pair.of(currentFilePath, fileName));
                    } else {
                        // 去除 base path, 保留设备路径
                        String machineFilePath = currentFilePath.replace(basePath, "");
                        machineMsjPath.add(new Pair<>(machineFilePath, msjId));
                    }
                }

                // 比较当前的程序是否与上一次一致，一致就不再校验，否则获取最后一台设备是否一致
                if (!programDeployListen(originPath)) {
                    // 未重新下发程序
                    log.info("检测到程序相同，没有换线，5s 后再查询。");
                    TimeUnit.SECONDS.sleep(5);

                    continue;
                }

                Set<String> machineNoCodes = machineMsjPath.stream().map(v -> v.getKey().split("/")[0])
                        .collect(Collectors.toSet());

                if (!ioSession.isActive()) {
                    running = false;
                    log.info("session 过期，即将停止查询换线结果");
                    continue;
                }

                String machineCode = (String) ioSession.getAttribute("machineCode");
                String machinePosition = (String) ioSession.getAttribute("machinePosition");
                int machineCount = Integer.parseInt(StrUtil.isBlank(machinePosition) ? "0" : machinePosition);

                if (machineNoCodes.size() == machineCount) {
                    String lastMachine = "mc" + (machineCount > 10 ? machineCount : ("0" + machineCount));

                    List<String> lastMachinePathKeys = new ArrayList<>();

                    // 说明： mc01/01/01 -> machine no/stage no/table no
                    lastMachinePathKeys.add(lastMachine + "/01/01/");
                    lastMachinePathKeys.add(lastMachine + "/01/02/");

                    boolean changeover = true;
                    // 确保设备数量一致
                    for (Pair<String, String> msjPathPair : machineMsjPath) {
                        if (lastMachinePathKeys.contains(msjPathPair.getKey())) {
                            changeover = msjPath.contains(msjPathPair.getValue());
                        }
                        if (!changeover) {
                            break;
                        }
                    }

                    if (!changeover) {
                        log.info("检测到程序不一致，5s 后继续比对");
                        TimeUnit.SECONDS.sleep(5);
                        continue;
                    } else {
                        String lane1MsjId = "";
                        String lane2MsjId = "";
                        for (Pair<String, String> pair : machineMsjPath) {
                            String key = pair.getKey();
                            if ((lastMachine + "/01/01/").equals(key)) {
                                lane1MsjId = pair.getValue();
                            }

                            if ((lastMachine + "/01/02/").equals(key)) {
                                lane2MsjId = pair.getValue();
                            }
                        }

                        String recipeReportStateLane1 = StrUtil.format(CommandConstant.CHANGEOVER_REPORT_TEMPLATE, RandomUtil.randomString(10), machineCode, 1, lane1MsjId);
                        String recipeReportStateLane2 = StrUtil.format(CommandConstant.CHANGEOVER_REPORT_TEMPLATE, RandomUtil.randomString(10), machineCode, 2, lane2MsjId);

                        ioSession.write(recipeReportStateLane1);
                        ioSession.write(recipeReportStateLane2);

                        // 清空表
                        deploymentRecordService.remove(new QueryWrapper<>());
                        saveCurrentFiles(originPath);
                    }
                }

                // Poll every 5 seconds
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("{} parse error, Please refer to redis for details", sftpChannel.getConfig().getRemotePath());
            }
        }

        log.warn("[{}] ssh watcher stopped...", DateUtil.now());
    }

    /**
     * 监听程序是否发生变化
     *
     * @param msjPaths 现在的程序文件信息
     */
    public boolean programDeployListen(List<Pair<String, String>> msjPaths) {
        List<ProgramDeploymentRecord> records = deploymentRecordService.list();

        // 上次的程序
        Set<String> mjsIds = records.stream().map(v -> v.getFileName().split("\\.")[0])
                .collect(Collectors.toSet());
        if (CollUtil.isEmpty(mjsIds)) {
            saveCurrentFiles(msjPaths);
            return false;
        }

        AtomicReference<Boolean> changed = new AtomicReference<>(false);
        msjPaths.forEach(pair -> {
            String msjId = pair.getValue().split("\\.")[0];
            if (!mjsIds.contains(msjId)) {
                changed.set(true);
            }
        });

        return changed.get();
    }

    private void saveCurrentFiles(List<Pair<String, String>> msjPaths) {
        List<ProgramDeploymentRecord> records = new ArrayList<>();
        String now = DateUtil.now();
        msjPaths.forEach(pair -> {
            ProgramDeploymentRecord record = new ProgramDeploymentRecord();
            record.setFilePath(pair.getKey());
            record.setFileName(pair.getValue());
            record.setDeployTime(now);
            records.add(record);
        });

        if (CollUtil.isNotEmpty(records)) {
            deploymentRecordService.saveBatch(records);
        }
    }


}
